package alibaba.client.cli;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

import alibaba.client.service.ClientBackupService;
import alibaba.client.service.ClientBackupServiceResponse;
import alibaba.client.service.DefaultClientBackupService;
import alibaba.common.CheckEntry;

public class AlibabaClient {
    
    private static final ClientBackupService service = new DefaultClientBackupService("localhost", 8080);
    
    public static void main(String[] args)  {
        
        if (args.length == 0) {
            System.out.println(CliAction.NO_COMMAND.getDescription());
            System.exit(-1);
        }
        
        doCommand(args);
    }
    
    private static void doCommand(String[] args) {

        CliAction action = parseAction(args);
        if (action == CliAction.ERROR) {
            System.out.println(CliAction.ERROR.getDescription());
            return;
        }
        
        String[] cmdArgs = Arrays.copyOfRange(args, 1, args.length);
        
        switch (action) {

        case BACKUP_FILE:
            handleBackupFile(cmdArgs, action);
            break;
        case RESTORE_FILE:
            handleRestoreFile(cmdArgs, action);
            break;
        case BACKUP_DIR:
            handleBackupDir(cmdArgs, action);
            break;
        case RESTORE_DIR:
            handleRestoreDir(cmdArgs, action);
            break;
        case LIST:
            handleListDir(cmdArgs, action);
            break;
        case CHECK:
            handleCheckDir(cmdArgs, action);
            break;
        case CREATE_SNANPSHOT:
            handleCreateSnapshot(cmdArgs, action);
            break;
        case DELETE_SNANPSHOT:
            handleDeleteSnapshot(cmdArgs, action);
            break;
        case HELP:
            printMainHelp();
            break;
        default:
            System.out.println("Not implememented yet.");
            break;
        }
        
    }
    
    private static CliAction parseAction(String[] args) {
        String commandString = args[0];
        
        if (commandEquals(commandString, CliAction.BACKUP_FILE)) {
            return CliAction.BACKUP_FILE;
        } else if (commandEquals(commandString,CliAction.RESTORE_FILE)) {
            return CliAction.RESTORE_FILE;
        } else if (commandEquals(commandString, CliAction.BACKUP_DIR)) {
            return CliAction.BACKUP_DIR;
        } else if (commandEquals(commandString, CliAction.RESTORE_DIR)) {
            return CliAction.RESTORE_DIR;
        } else if (commandEquals(commandString, CliAction.LIST)) {
            return CliAction.LIST;
        } else if (commandEquals(commandString, CliAction.CHECK)) {
            return CliAction.CHECK;
        } else if (commandEquals(commandString, CliAction.CREATE_SNANPSHOT)) {
            return CliAction.CREATE_SNANPSHOT;
        } else if (commandEquals(commandString, CliAction.DELETE_SNANPSHOT)) {
            return CliAction.DELETE_SNANPSHOT;
        } else if (commandEquals(commandString, CliAction.HELP)) {
            return CliAction.HELP;
        } else {
            return CliAction.ERROR;
        }
    }
    
    private static boolean commandEquals(String commandString, CliAction action) {
        return commandString.equals(action.getLongName()) || commandString.equals(action.getShortName());
    }
    
    private static void printMainHelp() {
        System.out.println("Usage: java -jar AlibabaClient.jar command [options]\n");
        System.out.println("Command name\t\tShort name\tDescription");
        System.out.println("-----------------------------------------------------------------------");
        for (CliAction action : CliAction.values()) {
            if (action != CliAction.ERROR && action != CliAction.NO_COMMAND) {
                printMainHelpLine(action);
            }
        }
    }
    
    private static void printMainHelpLine(CliAction action) {
        System.out.println(
                action.getLongName() + (action.getLongName().length() >=8 ? "\t\t" : "\t\t\t") + 
                action.getShortName() + "\t\t" + 
                action.getDescription()
        );
    }
    
    private static void handleBackupFile(String[] args, CliAction action) {
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }
        String fileName = commandLine.getOptionValue("file");
        String userName = commandLine.getOptionValue("user");
        ClientBackupServiceResponse response = service.backupFile(fileName,userName);
        if (response.isSuccess()) {
            System.out.println("File backed up successfully");
        } else {
            System.out.println(response.getMessage());
        }
    }

    private static void handleRestoreFile(String[] args, CliAction action) {
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }
        String fileName = commandLine.getOptionValue("file");
        String userName = commandLine.getOptionValue("user");
        String rawSnapshotNumber = commandLine.getOptionValue("snapshot");
        int snapshotNumber = safeParseInt(rawSnapshotNumber);
        
        if (snapshotNumber == -1) {
            printUsage(action.getLongName(), commandLine.getOptions());
        }
        
        ClientBackupServiceResponse response = service.restoreFile(fileName,userName, snapshotNumber);
        if (response.isSuccess()) {
            System.out.println("File restored successfully");
        } else {
            System.out.println(response.getMessage());
        }
    }
    
    private static void handleBackupDir(String[] args, CliAction action) {
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }
        String dirName = commandLine.getOptionValue("dir");
        String userName = commandLine.getOptionValue("user");
        
        ClientBackupServiceResponse response = service.backupDir(dirName,userName);
        if (response.isSuccess()) {
            System.out.println("Directory backed up successfully");
        } else {
            System.out.println(response.getMessage());
        }
    }
    
    private static void handleRestoreDir(String[] args, CliAction action) {
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }
        String dirName = commandLine.getOptionValue("dir");
        String userName = commandLine.getOptionValue("user");
        String rawSnapshotNumber = commandLine.getOptionValue("snapshot");
        int snapshotNumber = safeParseInt(rawSnapshotNumber);
        
        if (snapshotNumber == -1) {
            printUsage(action.getLongName(), commandLine.getOptions());
            return;
        }
        
        ClientBackupServiceResponse response = service.restoreDir(dirName,userName, snapshotNumber);
        if (response.isSuccess()) {
            System.out.println("Directory restored successfully");
        } else {
            System.out.println(response.getMessage());
        }
    }
    
    private static void handleListDir(String[] args, CliAction action) {
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }
        String dirName = commandLine.getOptionValue("dir");
        String userName = commandLine.getOptionValue("user");
        String rawSnapshotNumber = commandLine.getOptionValue("snapshot");
        int snapshotNumber = safeParseInt(rawSnapshotNumber);
        
        if (snapshotNumber == -1) {
            printUsage(action.getLongName(), commandLine.getOptions());
            return;
        }
        
        ClientBackupServiceResponse response = service.listDir(dirName,userName, snapshotNumber);
        if (response.isSuccess()) {
            @SuppressWarnings("unchecked")
            List<String> files = (List<String>)response.getResponseBody();
            for (String file : files) {
                System.out.println(file);
            }
        } else {
            System.out.println(response.getMessage());
        }
    }

    private static void handleCheckDir(String[] args, CliAction action) {
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }
        String dirName = commandLine.getOptionValue("dir");
        String userName = commandLine.getOptionValue("user");
        
        ClientBackupServiceResponse response = service.checkDir(dirName,userName);
        if (response.isSuccess()) {
            @SuppressWarnings("unchecked")
            List<CheckEntry> result = (List<CheckEntry>)response.getResponseBody();
            for (CheckEntry entry : result) {
                System.out.println(entry.toString());
            }
        } else {
            System.out.println(response.getMessage());
        }
    }
    
    private static void handleDeleteSnapshot(String[] args, CliAction action) {
        
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }

        String userName = commandLine.getOptionValue("user");
        String rawSnapshotNumber = commandLine.getOptionValue("snapshot");
        int snapshotNumber = safeParseInt(rawSnapshotNumber);
        
        if (snapshotNumber < 1) {
            printUsage(action.getLongName(), action.getOptions());
            return;
        }
        
        ClientBackupServiceResponse response = service.deleteSnapshot(userName, snapshotNumber);   
        if (response.isSuccess()) {
            System.out.println("Snapshot deleted successfully");
        } else {
            System.out.println(response.getMessage());
        }
        
    }

    private static void handleCreateSnapshot(String[] args, CliAction action) {
        
        CommandLine commandLine = parseCommand(args, action);
        if(commandLine == null) {
            return;
        }

        String userName = commandLine.getOptionValue("user");
        String rawSnapshotNumber = commandLine.getOptionValue("snapshot");
        int snapshotNumber = safeParseInt(rawSnapshotNumber);
        
        if (snapshotNumber < 1) {
            printUsage(action.getLongName(), action.getOptions());
            return;
        }

        ClientBackupServiceResponse response = service.createSnapshot(userName, snapshotNumber); 
        if (response.isSuccess()) {
            System.out.println("Snapshot created successfully");
        } else {
            System.out.println(response.getMessage());
        }
        
    }
    
    private static CommandLine parseCommand(String[] args, CliAction action) {
        
        CommandLineParser parser = new GnuParser();
        Options commandOptions = new Options();
        CommandLine commandLine;
        
        for (Option option : action.getOptions()) {
            commandOptions.addOption(option);
        }
        
        try {
            commandLine = parser.parse(commandOptions, args);
        } catch (ParseException exp) {
            System.out.println(exp.getMessage());
            printUsage(action.getLongName(), commandOptions);
            return null;
        }

        for (Option option : action.getOptions()) {
            if (option.isRequired()) {
                if (commandLine.getOptionValue(option.getLongOpt()).isEmpty()) {
                    printUsage(action.getLongName(), commandOptions);
                    return null;                   
                }
            }
        }
        
        return commandLine;
        
    }
    
    private static void printUsage(String longName, Options commandOptions) {
        HelpFormatter formatter = new HelpFormatter(); 
        formatter.printHelp(longName, commandOptions);
    }
    
    private static void printUsage(String longName, Option[] commandOptions) {
        Options options = new Options();
        for (Option option : commandOptions) {
            options.addOption(option);
        }
        printUsage(longName, options);
    }
    
    /**
     * 
     * @param rawInt On optional value to be passed as snapshot number
     * @return if rawInt is null number will be parsed as 0, other wise it
     * will try and parse the integer returning -1 on failure or negative number,
     * this should be propagated to the user and interpreted as a bad input
     */
    private static int safeParseInt(String rawInt) {
        if (rawInt == null) {
            return 0;
        }
        try {
            int result = Integer.parseInt(rawInt);
            return (result >= 0 ? result : -1);
        } catch (NumberFormatException e) {
            return -1;
        }
    }
    
}
